/*
 * acooly.cn Inc.
 * Copyright (c) 2016 All Rights Reserved.
 * create by zhike
 * date:2016年4月4日
 *
 */
package com.acooly.module.openapi.client.provider.wsbank.marshall;

import com.acooly.core.common.exception.BusinessException;
import com.acooly.core.utils.validate.Validators;
import com.acooly.module.openapi.client.api.exception.ApiClientException;
import com.acooly.module.openapi.client.provider.wsbank.OpenAPIClientWsbankProperties;
import com.acooly.module.openapi.client.provider.wsbank.WsbankConstants;
import com.acooly.module.openapi.client.provider.wsbank.domain.WsbankApiMessage;
import com.acooly.module.openapi.client.provider.wsbank.domain.WsbankRequest;
import com.acooly.module.openapi.client.provider.wsbank.enums.WsbankSignTypeEnum;
import com.acooly.module.openapi.client.provider.wsbank.utils.JsonMarshallor;
import com.acooly.module.openapi.client.provider.wsbank.utils.SignatureUtils;
import com.acooly.module.openapi.client.provider.wsbank.utils.XmlUtils;
import com.acooly.module.safety.exception.SignatureVerifyException;
import com.acooly.module.safety.key.KeyLoadManager;
import com.acooly.module.safety.signature.SignerFactory;
import com.acooly.module.safety.support.KeyPair;
import lombok.extern.slf4j.Slf4j;
import org.apache.xml.security.signature.XMLSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.w3c.dom.Document;

import java.security.PrivateKey;
import java.security.PublicKey;

/**
 * @author zhike
 */
@Slf4j
public class WsbankMarshallSupport {

    @Autowired
    protected OpenAPIClientWsbankProperties openAPIClientJytProperties;

    @Autowired
    protected SignerFactory signerFactory;

    @Autowired
    private KeyLoadManager keyStoreLoadManager;

    private static JsonMarshallor jsonMarshallor = JsonMarshallor.INSTANCE;

    protected OpenAPIClientWsbankProperties getProperties() {
        return openAPIClientJytProperties;
    }

    public SignerFactory getSignerFactory() {
        return signerFactory;
    }

    protected String doMarshall(WsbankRequest source) {
        doVerifyParam(source);
        String signXml = XmlUtils.trimXml(XmlUtils.toXml(source));
        String requestXml = doSign(signXml, WsbankSignTypeEnum.request_sign);
        return requestXml;
    }

    /**
     * 校验参数
     *
     * @param source
     */
    protected void doVerifyParam(WsbankApiMessage source) {
        try {
            source.doCheck();
            Validators.assertJSR303(source);
        } catch (Exception e) {
            throw new ApiClientException(e.getMessage());
        }
    }

    protected String doSign(String waitForSign,WsbankSignTypeEnum signType) {
        try {
            Document doc = XmlUtils.parseDocumentByString(waitForSign);
            KeyPair keyPair = getKeyPair();
            PrivateKey privateKey = keyPair.getPrivateKey();
            String result = "";
            if (signType == WsbankSignTypeEnum.request_sign) {
                result = SignatureUtils.signXmlElement(privateKey, doc, "request",
                        XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA256, 2);
            }else {
                result = SignatureUtils.signXmlElement(privateKey, doc, "response",
                        XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA256, 2);
            }
            return result;
        }catch (Exception e) {
            throw new ApiClientException("签名异常："+e.getMessage());
        }
    }

    /**
     * 验签
     *
     * @param plain
     * @return
     */
    protected void doVerifySign(String plain) {
        try {
            Document doc = XmlUtils.parseDocumentByString(plain);
            PublicKey publicKey = getKeyPair().getPublicKey();
            boolean verifySignResult = SignatureUtils.verifyXmlElement(publicKey,doc);
            if(!verifySignResult) {
                log.info("验签失败，待签字符串:{}",plain);
                throw new SignatureVerifyException("待签字符串:" + plain);
            }
        } catch (Exception e) {
            throw new BusinessException("验签失败："+e.getMessage());
        }
    }


    protected KeyPair getKeyPair() {
        return keyStoreLoadManager.load(WsbankConstants.PROVIDER_DEF_PRINCIPAL, WsbankConstants.PROVIDER_NAME);
    }

    protected void beforeMarshall(WsbankApiMessage message) {

    }
}
